package com.honkentuber.chat;

import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javax.jdo.annotations.Element;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.repackaged.com.google.common.collect.Sets;
import com.honkentuber.MyUser;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Room {
  private final static Logger LOG = Logger.getLogger(Room.class.getName());

  @PrimaryKey
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Key key;

  @Persistent
  private Key creatorKey;

  @Persistent
  private String name;

  @Persistent(mappedBy = "room")
  @Element(dependent = "true")
  // @Order(mappedBy = "lastAccessMs")
  private List<Presence> presences;

  /**
   * @param creator Creator.
   * @param name Room name.
   */
  public Room(Key creatorKey, String name) {
    this.creatorKey = creatorKey;
    this.name = name;
  }

  public Key getKey() {
    return key;
  }

  public Key getCreatorKey() {
    return creatorKey;
  }

  public String getName() {
    return name;
  }

  public String toString() {
    return key + "|" + name + "|" + creatorKey;
  }

  /**
   * TODO(ptucker): in ascending order of last access time.
   * @return Present users.
   */
  public List<Presence> getPresences() {
    return presences;
  }

  private static long MAX_PRESENCE_AGE_MS = 5 * 60 * 1000;

  // TODO(ptucker) add lastMessage to presence

  public void updatePresence(MyUser user) {
    long now = System.currentTimeMillis();
    long threshold = now - MAX_PRESENCE_AGE_MS;

    Presence userPresence = null;
    Set<Presence> toPurge = Sets.newHashSet();
    for (Presence presence : presences) {
      if (presence.getUserKey() == null) {
        // This should be unreachable, but we'll go ahead and clean it up here.
        toPurge.add(presence);
        LOG.severe("null user in presence for room " + key);
      } else if (user != null && presence.getUserKey().equals(user.getKey())) {
        userPresence = presence;
      } else if (presence.getLastAccessMs() < threshold) {
        // Prune old presences.
        toPurge.add(presence);
        LOG.config("prune old presence " + presence);
      }
    }
    presences.removeAll(toPurge);

    if (user != null && userPresence == null) {
      userPresence = new Presence(user.getKey(), this);
      presences.add(userPresence);
      LOG.config("adding new presence " + userPresence);
    }

    if (userPresence != null) {
      userPresence.setLastAccessMs(now);
      LOG.config("updating presence " + userPresence);
    }
  }

  @SuppressWarnings("unchecked")
  public JSONObject toJson() {
    JSONObject jsonRoom = new JSONObject();
    jsonRoom.put("key", KeyFactory.keyToString(key));
    if (creatorKey != null) {
      jsonRoom.put("creatorKey", KeyFactory.keyToString(creatorKey));
    }
    jsonRoom.put("name", name);
    JSONArray jsonPresences = new JSONArray();
    for (Presence presence : presences) {
      jsonPresences.add(KeyFactory.keyToString(presence.getUserKey()));
    }
    jsonRoom.put("present", jsonPresences);
    return jsonRoom;
  }

  @SuppressWarnings("unchecked")
  public static JSONObject toJson(List<Room> rooms) {
    // TODO(ptucker) move to Room.toJson
    // Build response, a single array of rooms.
    JSONObject jsonResponse = new JSONObject();
    if (!rooms.isEmpty()) {
      JSONArray jsonRooms = new JSONArray();
      for (Room room : rooms) {
        jsonRooms.add(room.toJson());
      }
      jsonResponse.put("rooms", jsonRooms);
    }
    return jsonResponse;
  }

}